Read counts data
Load and transpose counts file (Sample IDs as header and miRNA IDs as
first column)
oriCountsFile <- read.csv(file = './mature_counts.csv')
countsFile <- as.data.frame(t(oriCountsFile)) # transpose oriCountsFile
countsFile <- janitor::row_to_names(countsFile, row_number=1)
countsFile <- tibble::rownames_to_column(countsFile, var="miR")
countsFile$miR <- chartr('.', '-', countsFile$miR) # replace '.' with '-' in miR
countsFile
Update the IDs
update <- miRNAVersionConvert(countsFile$miR)
head(update)
length(unique(countsFile$miR))
length(unique(update[,"OriginalName"]))
length(unique(update[,"TargetName"]))
Fetch the mirbase22 IDs and sequences
mrbse <- read.table("../../miRNA/mirbase/mature_homo-sapiens_dataframe.txt", header = T, sep = "\t")
mrbse <- mrbse[-c(1,2),] # The first two rows don't match up with mirbase, are the only duplicates, and are the only sequences with T's for some reason. I'll remove them.
head(mrbse)
Create microRNA Mapping table and Update Countfile miRNA Row
Names
mrbse2 <- merge(mrbse, update, by.x = 1, by.y = 2, all.x = F, all.y = T)
mrbse2 <- mrbse2[,-4]
dim(mrbse2)
[1] 2423 3
head(mrbse2)
rownames(mrbse2)<- mrbse2[,"OriginalName"]
rownames(countsFile)<-countsFile[,1]
rownames(countsFile)<- mrbse2[rownames(countsFile), "Name"]
Create Row for each miRNA and Merge this with the data
mergedData <- matrix(0, nrow = dim(mrbse)[1], ncol= 302)
mergedData <- data.frame(mergedData)
mergedData<- cbind(mrbse, mergedData)
colnames(mergedData)<- c("Name", "Seq", colnames(countsFile)[-1])
rownames(mergedData)<-mergedData[,1]
mergedData[rownames(countsFile), colnames(countsFile)[-1]]<- countsFile[,-1]
#mergedData <- merge(mrbse2, countsFile, by.x = 3, by.y = 1, all.x = T, all.y = F)
dim(mergedData)
[1] 2656 304
head(mergedData)
#mergedData <- mergedData[,-1] # Remove the original (old) IDs
mergedData[1:5,1:5]
mergedData
Quintize the data
For each column: assign each miRNA a value 1-5 depending on which of
the 20th percentiles it falls into. If the value is 0, it remains a
0
# Given a column, assign a number to each element from 0-5. All
# 0s get a 0, and the rest get a value according to the 20th percentile
# that it falls in among the non-zero values.
quintize <- function(vec) {
qntls <- c(0, quantile(vec[which(vec != 0)], 0.2*(1:4)))
vec2 <- sapply(vec, function(x) {
if (x == 0) return(0)
else return(max(which(x > qntls)))
})
return(vec2)
}
mergedData[,3:ncol(mergedData)] = lapply(mergedData[,3:ncol(mergedData)], FUN = function(y){as.numeric(y)})
quintizedData <- apply(mergedData[,-c(1:2)], 2, quintize)
quintizedData <- cbind(mergedData[,1:2], quintizedData)
quintizedData[1:5,1:5]
Combine the replicates
Get the meta information
meta <- read.table("./meta.txt", header = T, sep = "\t")
meta$Sample_ID <- gsub("BioSample: https://www.ncbi.nlm.nih.gov/biosample/", "", meta$X.Sample_relation) # extract only SAMN IDs from X.Sample_relation
Create Disease, Tissue, and Group (Tissue_Disease) columns
meta$Tissue <- str_replace(meta$X.Sample_source_name_ch1, "tissue: ", "")
meta$Tissue <- str_replace_all(meta$Tissue, " ", ".")
meta$Tissue <- tolower(meta$Tissue)
meta$Disease <- str_replace_all(meta$X.Sample_characteristics_ch1, "disease status: ", "")
meta$Disease <- str_replace_all(meta$Disease, " ", ".")
meta$Group <- apply(meta[,c(ncol(meta)-1,ncol(meta))], 1, function(x) paste(x[1], x[2], sep="_"))
meta$Group <- str_replace_all(meta$Group, "_Adjacent.Normal", "")
meta$Disease <- str_replace_all(meta$Disease, "Adjacent Normal", "")
Grouping: Combine replicates (samples that have the same type)
uniqueTissues <- unique(meta$Group)
meta$Sample_ID %in% names(quintizedData)
[1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
[28] TRUE TRUE TRUE TRUE TRUE
combinedData <- lapply(uniqueTissues, function(x) {
df <- as.data.frame(quintizedData[,meta$Sample_ID[which(meta$Group == x)]])
return(rowMeans(df))
})
names(combinedData) <- uniqueTissues
combinedData <- do.call(cbind, combinedData)
combinedData <- cbind(quintizedData[,1:2], combinedData)
Ensure the terms are standardized
Ensure that all the group names appear in the ontology or the
corrections file.
Get the ontology and correction files
ont <- read.table("../ontology.txt", header = F, sep = "\t")
corr <- read.table("../corrections.txt", header = T, sep = "\t")
Aim to have 0 as a result, meaning all terms are either already in
the ontology or the correction file. Or else: update correction file or
ontology file, or both.
trms <- unique(unlist(strsplit(names(combinedData)[3:ncol(combinedData)], "_"))) # Splits the composite terms that contain both tissue+disease
if (length(which(trms %in% union(corr[,1], unique(unlist(ont[,c(1,3)]))))) != 0){
trms[-which(trms %in% union(corr[,1], unique(unlist(ont[,c(1,3)]))))] # Which terms aren't in the ontology or the corrected terms
} else {
trms
}
character(0)
Found 2 new terms: HCC, Adjacent.Normal. Terms are updated in
ontology file and correction file, we just need to update them with the
corrected terms.
colnames(combinedData) <- sapply(colnames(combinedData), function(z) {
y <- strsplit(z, "_")[[1]]
retVal <- sapply(y, function(x) {
if (x %in% corr$currentTerm) {
return(corr$correctedTerm[match(x, corr$currentTerm)])
} else {
return(x)
}
})
return(paste(retVal, collapse = "_"))
})
Write to file
write.table(data_long, paste(sep="", result_path,"/varghese_longData.txt"), sep = "\t", row.names = F, col.names = T, quote = F)
write.table(unique(data_long[,1:3]), paste(sep="", result_path,"/varghese_miRNAs.txt"), sep = "\t", row.names = F, col.names = T, quote = F)
writeLines(as.character(unique(data_long$Tissue)), paste(sep="", result_path,"/varghese_tissues.txt"))
LS0tCnRpdGxlOiAiUHJvY2Vzc2luZyBWYXJnaGVzZSBleHByZXNzaW9uIGRhdGEiCmF1dGhvcjogIkdpdHRhIEVrYXB1dGVyaSwgQW5uZS1DaHJpc3RpbiBIYXVzY2hpbGQiCmRhdGU6ICIyOS8wOC8yMDIyIiAKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShtaVJCYXNlQ29udmVydGVyKQpsaWJyYXJ5KHJlc2hhcGUyKQpyb290X3BhdGggPC0gIi4vIgpyZXN1bHRfcGF0aCA8LSBwYXN0ZShyb290X3BhdGgsICIvZGF0YS9taVJOQV9maW5hbCIsIHNlcD0iIikKa25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSBwYXN0ZShzZXA9IiIsIHJvb3RfcGF0aCwgImRhdGEvbWlSTkEvdmFyZ2hlc2VEYXRhLyIpKQpzZXR3ZChwYXN0ZShzZXA9IiIsIHJvb3RfcGF0aCwgImRhdGEvbWlSTkEvdmFyZ2hlc2VEYXRhLyIpKQpgYGAKCiMjIyBSZWFkIGNvdW50cyBkYXRhCgpMb2FkIGFuZCB0cmFuc3Bvc2UgY291bnRzIGZpbGUgKFNhbXBsZSBJRHMgYXMgaGVhZGVyIGFuZCBtaVJOQSBJRHMgYXMgZmlyc3QgY29sdW1uKQpgYGB7cn0Kb3JpQ291bnRzRmlsZSA8LSByZWFkLmNzdihmaWxlID0gJy4vbWF0dXJlX2NvdW50cy5jc3YnKQpjb3VudHNGaWxlIDwtIGFzLmRhdGEuZnJhbWUodChvcmlDb3VudHNGaWxlKSkgIyB0cmFuc3Bvc2Ugb3JpQ291bnRzRmlsZQpjb3VudHNGaWxlIDwtIGphbml0b3I6OnJvd190b19uYW1lcyhjb3VudHNGaWxlLCByb3dfbnVtYmVyPTEpCmNvdW50c0ZpbGUgPC0gdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oY291bnRzRmlsZSwgdmFyPSJtaVIiKQpjb3VudHNGaWxlJG1pUiA8LSBjaGFydHIoJy4nLCAnLScsIGNvdW50c0ZpbGUkbWlSKSAjIHJlcGxhY2UgJy4nIHdpdGggJy0nIGluIG1pUgpjb3VudHNGaWxlCmBgYAoKIyMjIFVwZGF0ZSB0aGUgSURzIAoKYGBge3J9CnVwZGF0ZSA8LSBtaVJOQVZlcnNpb25Db252ZXJ0KGNvdW50c0ZpbGUkbWlSKQpoZWFkKHVwZGF0ZSkKbGVuZ3RoKHVuaXF1ZShjb3VudHNGaWxlJG1pUikpCmxlbmd0aCh1bmlxdWUodXBkYXRlWywiT3JpZ2luYWxOYW1lIl0pKQpsZW5ndGgodW5pcXVlKHVwZGF0ZVssIlRhcmdldE5hbWUiXSkpCgpgYGAKCkZldGNoIHRoZSBtaXJiYXNlMjIgSURzIGFuZCBzZXF1ZW5jZXMKCmBgYHtyfQptcmJzZSA8LSByZWFkLnRhYmxlKCIuLi8uLi9taVJOQS9taXJiYXNlL21hdHVyZV9ob21vLXNhcGllbnNfZGF0YWZyYW1lLnR4dCIsIGhlYWRlciA9IFQsIHNlcCA9ICJcdCIpCm1yYnNlIDwtIG1yYnNlWy1jKDEsMiksXSAjIFJlbW92ZSB0aGUgZmlyc3QgdHdvIHJvd3MgdGhhdCBkb24ndCBtYXRjaCB1cCB3aXRoIG1pcmJhc2UgYW5kIGFyZSB0aGUgb25seSBkdXBsaWNhdGVzLCBhbmQgYXJlIHRoZSBvbmx5IHNlcXVlbmNlcyB3aXRoIFQncyBmb3Igc29tZSByZWFzb24uCmhlYWQobXJic2UpCmBgYAoKQ3JlYXRlIG1pY3JvUk5BIE1hcHBpbmcgdGFibGUgYW5kIFVwZGF0ZSBDb3VudGZpbGUgbWlSTkEgUm93IE5hbWVzCmBgYHtyfQptcmJzZTIgPC0gbWVyZ2UobXJic2UsIHVwZGF0ZSwgYnkueCA9IDEsIGJ5LnkgPSAyLCBhbGwueCA9IEYsIGFsbC55ID0gVCkKbXJic2UyIDwtIG1yYnNlMlssLTRdCmRpbShtcmJzZTIpCmhlYWQobXJic2UyKQpyb3duYW1lcyhtcmJzZTIpPC0gbXJic2UyWywiT3JpZ2luYWxOYW1lIl0Kcm93bmFtZXMoY291bnRzRmlsZSk8LWNvdW50c0ZpbGVbLDFdCnJvd25hbWVzKGNvdW50c0ZpbGUpPC0gbXJic2UyW3Jvd25hbWVzKGNvdW50c0ZpbGUpLCAiTmFtZSJdCmBgYAoKCkNyZWF0ZSBSb3cgZm9yIGVhY2ggbWlSTkEgYW5kIE1lcmdlIHRoaXMgd2l0aCB0aGUgZGF0YQoKYGBge3J9Cm1lcmdlZERhdGEgPC0gbWF0cml4KDAsIG5yb3cgPSBkaW0obXJic2UpWzFdLCBuY29sPSAzMDIpCm1lcmdlZERhdGEgPC0gZGF0YS5mcmFtZShtZXJnZWREYXRhKQptZXJnZWREYXRhPC0gY2JpbmQobXJic2UsIG1lcmdlZERhdGEpCmNvbG5hbWVzKG1lcmdlZERhdGEpPC0gYygiTmFtZSIsICJTZXEiLCBjb2xuYW1lcyhjb3VudHNGaWxlKVstMV0pCnJvd25hbWVzKG1lcmdlZERhdGEpPC1tZXJnZWREYXRhWywxXQptZXJnZWREYXRhW3Jvd25hbWVzKGNvdW50c0ZpbGUpLCBjb2xuYW1lcyhjb3VudHNGaWxlKVstMV1dPC0gY291bnRzRmlsZVssLTFdCiNtZXJnZWREYXRhIDwtIG1lcmdlKG1yYnNlMiwgY291bnRzRmlsZSwgYnkueCA9IDMsIGJ5LnkgPSAxLCBhbGwueCA9IFQsIGFsbC55ID0gRikKZGltKG1lcmdlZERhdGEpCmhlYWQobWVyZ2VkRGF0YSkKI21lcmdlZERhdGEgPC0gbWVyZ2VkRGF0YVssLTFdICMgUmVtb3ZlIHRoZSBvcmlnaW5hbCAob2xkKSBJRHMKbWVyZ2VkRGF0YVsxOjUsMTo1XQptZXJnZWREYXRhCmBgYAoKCgoKIyMjIFF1aW50aXplIHRoZSBkYXRhCgpGb3IgZWFjaCBjb2x1bW46IGFzc2lnbiBlYWNoIG1pUk5BIGEgdmFsdWUgMS01IGRlcGVuZGluZyBvbiB3aGljaCBvZiB0aGUgMjB0aCBwZXJjZW50aWxlcyBpdCBmYWxscyBpbnRvLiBJZiB0aGUgdmFsdWUgaXMgMCwgaXQgcmVtYWlucyBhIDAKCmBgYHtyfQojIEdpdmVuIGEgY29sdW1uLCBhc3NpZ24gYSBudW1iZXIgdG8gZWFjaCBlbGVtZW50IGZyb20gMC01LiBBbGwKIyAwcyBnZXQgYSAwLCBhbmQgdGhlIHJlc3QgZ2V0IGEgdmFsdWUgYWNjb3JkaW5nIHRvIHRoZSAyMHRoIHBlcmNlbnRpbGUKIyB0aGF0IGl0IGZhbGxzIGluIGFtb25nIHRoZSBub24temVybyB2YWx1ZXMuCnF1aW50aXplIDwtIGZ1bmN0aW9uKHZlYykgewogIHFudGxzIDwtIGMoMCwgcXVhbnRpbGUodmVjW3doaWNoKHZlYyAhPSAwKV0sIDAuMiooMTo0KSkpCiAgdmVjMiA8LSBzYXBwbHkodmVjLCBmdW5jdGlvbih4KSB7CiAgICBpZiAoeCA9PSAwKSByZXR1cm4oMCkKICAgIGVsc2UgcmV0dXJuKG1heCh3aGljaCh4ID4gcW50bHMpKSkKICB9KQogIHJldHVybih2ZWMyKQp9CmBgYAoKYGBge3J9Cm1lcmdlZERhdGFbLDM6bmNvbChtZXJnZWREYXRhKV0gPSBsYXBwbHkobWVyZ2VkRGF0YVssMzpuY29sKG1lcmdlZERhdGEpXSwgRlVOID0gZnVuY3Rpb24oeSl7YXMubnVtZXJpYyh5KX0pCnF1aW50aXplZERhdGEgPC0gYXBwbHkobWVyZ2VkRGF0YVssLWMoMToyKV0sIDIsIHF1aW50aXplKQpxdWludGl6ZWREYXRhIDwtIGNiaW5kKG1lcmdlZERhdGFbLDE6Ml0sIHF1aW50aXplZERhdGEpCnF1aW50aXplZERhdGFbMTo1LDE6NV0KYGBgCgoKCgojIyMgQ29tYmluZSB0aGUgcmVwbGljYXRlcwoKR2V0IHRoZSBtZXRhIGluZm9ybWF0aW9uCgpgYGB7cn0KbWV0YSA8LSByZWFkLnRhYmxlKCIuL21ldGEudHh0IiwgaGVhZGVyID0gVCwgc2VwID0gIlx0IikKbWV0YSRTYW1wbGVfSUQgPC0gZ3N1YigiQmlvU2FtcGxlOiBodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2Jpb3NhbXBsZS8iLCAiIiwgbWV0YSRYLlNhbXBsZV9yZWxhdGlvbikgIyBleHRyYWN0IG9ubHkgU0FNTiBJRHMgZnJvbSBYLlNhbXBsZV9yZWxhdGlvbgpgYGAKCgpDcmVhdGUgRGlzZWFzZSwgVGlzc3VlLCBhbmQgR3JvdXAgKFRpc3N1ZV9EaXNlYXNlKSBjb2x1bW5zCgpgYGB7cn0KbWV0YSRUaXNzdWUgPC0gc3RyX3JlcGxhY2UobWV0YSRYLlNhbXBsZV9zb3VyY2VfbmFtZV9jaDEsICJ0aXNzdWU6ICIsICIiKQptZXRhJFRpc3N1ZSA8LSBzdHJfcmVwbGFjZV9hbGwobWV0YSRUaXNzdWUsICIgIiwgIi4iKQptZXRhJFRpc3N1ZSA8LSB0b2xvd2VyKG1ldGEkVGlzc3VlKQptZXRhJERpc2Vhc2UgPC0gc3RyX3JlcGxhY2VfYWxsKG1ldGEkWC5TYW1wbGVfY2hhcmFjdGVyaXN0aWNzX2NoMSwgImRpc2Vhc2Ugc3RhdHVzOiAiLCAiIikKbWV0YSREaXNlYXNlIDwtIHN0cl9yZXBsYWNlX2FsbChtZXRhJERpc2Vhc2UsICIgIiwgIi4iKQptZXRhJEdyb3VwIDwtIGFwcGx5KG1ldGFbLGMobmNvbChtZXRhKS0xLG5jb2wobWV0YSkpXSwgMSwgZnVuY3Rpb24oeCkgcGFzdGUoeFsxXSwgeFsyXSwgc2VwPSJfIikpCm1ldGEkR3JvdXAgPC0gc3RyX3JlcGxhY2VfYWxsKG1ldGEkR3JvdXAsICJfQWRqYWNlbnQuTm9ybWFsIiwgIiIpCm1ldGEkRGlzZWFzZSA8LSBzdHJfcmVwbGFjZV9hbGwobWV0YSREaXNlYXNlLCAiQWRqYWNlbnQgTm9ybWFsIiwgIiIpCmBgYAoKCkdyb3VwaW5nOiBDb21iaW5lIHJlcGxpY2F0ZXMgKHNhbXBsZXMgdGhhdCBoYXZlIHRoZSBzYW1lIHR5cGUpCgpgYGB7cn0KdW5pcXVlVGlzc3VlcyA8LSB1bmlxdWUobWV0YSRHcm91cCkKbWV0YSRTYW1wbGVfSUQgJWluJSBuYW1lcyhxdWludGl6ZWREYXRhKQpjb21iaW5lZERhdGEgPC0gbGFwcGx5KHVuaXF1ZVRpc3N1ZXMsIGZ1bmN0aW9uKHgpIHsKICBkZiA8LSBhcy5kYXRhLmZyYW1lKHF1aW50aXplZERhdGFbLG1ldGEkU2FtcGxlX0lEW3doaWNoKG1ldGEkR3JvdXAgPT0geCldXSkKICByZXR1cm4ocm93TWVhbnMoZGYpKQp9KQpuYW1lcyhjb21iaW5lZERhdGEpIDwtIHVuaXF1ZVRpc3N1ZXMKY29tYmluZWREYXRhIDwtIGRvLmNhbGwoY2JpbmQsIGNvbWJpbmVkRGF0YSkKY29tYmluZWREYXRhIDwtIGNiaW5kKHF1aW50aXplZERhdGFbLDE6Ml0sIGNvbWJpbmVkRGF0YSkKYGBgCgoKIyMjIEVuc3VyZSB0aGUgdGVybXMgYXJlIHN0YW5kYXJkaXplZApFbnN1cmUgdGhhdCBhbGwgdGhlIGdyb3VwIG5hbWVzIGFwcGVhciBpbiB0aGUgb250b2xvZ3kgb3IgdGhlIGNvcnJlY3Rpb25zIGZpbGUuCgpHZXQgdGhlIG9udG9sb2d5IGFuZCBjb3JyZWN0aW9uIGZpbGVzCgpgYGB7cn0Kb250IDwtIHJlYWQudGFibGUoIi4uL29udG9sb2d5LnR4dCIsIGhlYWRlciA9IEYsIHNlcCA9ICJcdCIpCmNvcnIgPC0gcmVhZC50YWJsZSgiLi4vY29ycmVjdGlvbnMudHh0IiwgaGVhZGVyID0gVCwgc2VwID0gIlx0IikKYGBgCgpBaW0gdG8gaGF2ZSAwIGFzIGEgcmVzdWx0LCBtZWFuaW5nIGFsbCB0ZXJtcyBhcmUgZWl0aGVyIGFscmVhZHkgaW4gdGhlIG9udG9sb2d5IG9yIHRoZSBjb3JyZWN0aW9uIGZpbGUuIE9yIGVsc2U6IHVwZGF0ZSBjb3JyZWN0aW9uIGZpbGUgb3Igb250b2xvZ3kgZmlsZSwgb3IgYm90aC4KCmBgYHtyfQp0cm1zIDwtIHVuaXF1ZSh1bmxpc3Qoc3Ryc3BsaXQobmFtZXMoY29tYmluZWREYXRhKVszOm5jb2woY29tYmluZWREYXRhKV0sICJfIikpKSAjIFNwbGl0cyB0aGUgY29tcG9zaXRlIHRlcm1zIHRoYXQgY29udGFpbiBib3RoIHRpc3N1ZStkaXNlYXNlCmlmIChsZW5ndGgod2hpY2godHJtcyAlaW4lIHVuaW9uKGNvcnJbLDFdLCB1bmlxdWUodW5saXN0KG9udFssYygxLDMpXSkpKSkpICE9IDApewogIHRybXNbLXdoaWNoKHRybXMgJWluJSB1bmlvbihjb3JyWywxXSwgdW5pcXVlKHVubGlzdChvbnRbLGMoMSwzKV0pKSkpXSAjIFdoaWNoIHRlcm1zIGFyZW4ndCBpbiB0aGUgb250b2xvZ3kgb3IgdGhlIGNvcnJlY3RlZCB0ZXJtcwp9IGVsc2UgewogIHRybXMKfQpgYGAKRm91bmQgMiBuZXcgdGVybXM6IEhDQywgQWRqYWNlbnQuTm9ybWFsLiBUZXJtcyBhcmUgdXBkYXRlZCBpbiBvbnRvbG9neSBmaWxlIGFuZCBjb3JyZWN0aW9uIGZpbGUsIHdlIGp1c3QgbmVlZCB0byB1cGRhdGUgdGhlbSB3aXRoIHRoZSBjb3JyZWN0ZWQgdGVybXMuCgpgYGB7cn0KY29sbmFtZXMoY29tYmluZWREYXRhKSA8LSBzYXBwbHkoY29sbmFtZXMoY29tYmluZWREYXRhKSwgZnVuY3Rpb24oeikgewogIHkgPC0gc3Ryc3BsaXQoeiwgIl8iKVtbMV1dCiAgcmV0VmFsIDwtIHNhcHBseSh5LCBmdW5jdGlvbih4KSB7CiAgICBpZiAoeCAlaW4lIGNvcnIkY3VycmVudFRlcm0pIHsKICAgICAgcmV0dXJuKGNvcnIkY29ycmVjdGVkVGVybVttYXRjaCh4LCBjb3JyJGN1cnJlbnRUZXJtKV0pCiAgICB9IGVsc2UgewogICAgICByZXR1cm4oeCkKICAgIH0KICB9KQogIHJldHVybihwYXN0ZShyZXRWYWwsIGNvbGxhcHNlID0gIl8iKSkKfSkKYGBgCgojIyMgQ29udmVydCB0byBsb25nIGZvcm1hdApBZGQgdGhlIENhbm9uaWNhbCBjb2x1bW4gYW5kIFNvdXJjZSBjb2x1bW4KCmBgYHtyfQpkYXRhIDwtIGNvbWJpbmVkRGF0YSAjIGFsbCB0aXNzdWVzIGhhdmUgdG8gZXhpc3QgaW4gb250b2xvZ3kgb3IgY29ycmVjdGlvbiBmaWxlcwpkYXRhJENhbm9uaWNhbCA8LSBzYXBwbHkoZGF0YSRTZXEsIGZ1bmN0aW9uKHgpIGlmICh4ICVpbiUgbXJic2UkU2VxKSByZXR1cm4oVCkgZWxzZSByZXR1cm4oRikpCmRhdGEkU291cmNlIDwtICJWYXJnaGVzZSoiCmRhdGEgPC0gZGF0YVssYygxLDIsbmNvbChkYXRhKS0xLG5jb2woZGF0YSksMzoobmNvbChkYXRhKS0yKSldICMgU2V0IGNvbHVtbnMgYWxpZ25tZW50CmhlYWQoZGF0YSkKYGBgCgoKQ29udmVydCB0byBsb25nIGZvcm1hdAoKYGBge3J9CmRhdGFfbG9uZyA8LSBtZWx0KGRhdGEsIGlkLnZhcnM9YygiTmFtZSIsICJTZXEiLCAiQ2Fub25pY2FsIiwgIlNvdXJjZSIpKQpgYGAKCkJpbmFyaXphdGlvbgpgYGB7cn0KZGF0YV9sb25nJEJpbmFyeSA8LSBzYXBwbHkoZGF0YV9sb25nJHZhbHVlLCBmdW5jdGlvbih4KSBpZiAoeCA9PSAwKSByZXR1cm4oMCkgZWxzZSByZXR1cm4oMSkpCm5hbWVzKGRhdGFfbG9uZykgPC0gYygibWlSIiwgIlNlcSIsICJDYW5vbmljYWwiLCAiU291cmNlIiwgIlRpc3N1ZSIsICJTY2FsZSIsICJCaW5hcnkiKQpgYGAKCgojIyMgV3JpdGUgdG8gZmlsZQoKYGBge3J9CndyaXRlLnRhYmxlKGRhdGFfbG9uZywgcGFzdGUoc2VwPSIiLCByZXN1bHRfcGF0aCwiL3ZhcmdoZXNlX2xvbmdEYXRhLnR4dCIpLCBzZXAgPSAiXHQiLCByb3cubmFtZXMgPSBGLCBjb2wubmFtZXMgPSBULCBxdW90ZSA9IEYpCndyaXRlLnRhYmxlKHVuaXF1ZShkYXRhX2xvbmdbLDE6M10pLCBwYXN0ZShzZXA9IiIsIHJlc3VsdF9wYXRoLCIvdmFyZ2hlc2VfbWlSTkFzLnR4dCIpLCBzZXAgPSAiXHQiLCByb3cubmFtZXMgPSBGLCBjb2wubmFtZXMgPSBULCBxdW90ZSA9IEYpCndyaXRlTGluZXMoYXMuY2hhcmFjdGVyKHVuaXF1ZShkYXRhX2xvbmckVGlzc3VlKSksIHBhc3RlKHNlcD0iIiwgcmVzdWx0X3BhdGgsIi92YXJnaGVzZV90aXNzdWVzLnR4dCIpKQpgYGAK